using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Reflection;
using System.Resources;
using System.Runtime.InteropServices;
using System.Text;
using System.Web.Services.Description;
using System.Web.Services.Discovery;
using System.Web.Services.Protocols;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using Microsoft.CSharp;
using Microsoft.VisualBasic;
using Thinktecture.Tools.Web.Services.Wscf.Environment;

namespace Thinktecture.Tools.Web.Services.CodeGeneration
{
	[ComVisible(true)]
	[Guid("56AAC898-4C47-44d5-B54D-B7F45DE949BC"), ProgId("Thinktecture.Tools.Web.Services.CodeGeneration.WebServiceCodeGenerator")]
	[StructLayout(LayoutKind.Sequential)]
	public class WebServiceCodeGenerator
	{
		private string _wsdl;
		private string _wsdlSource;
		private string _language;
		private bool _genProps;
		private string _appConfigFile;
		private bool _accessSOAP;
		private bool _endpointURLConfig;
		private string _fullyQServiceClass;
		private bool _clientSide;
		private bool _serializableClasses;
		private bool _enableValidation;
		private bool _generateCollections;
		private string _serviceFile;
		private ServiceDescriptionImporter sdi;
		private ServiceDescriptionImporter sdi2;			
		private bool _generateMultipleFiles;	// BDS: Added this new flag to hold preference for the multiple file genaration.		
		private bool _generateInterface;		// BDS: Added this new flag to hold the preference for the Interface generarion.		
		private bool _generateHelp;				// BDS: Added this new flag to hold the preference for the Help files generarion.		
		private string _stubFile;				// BDS: Added two new string variables to hold the proxy and stub file names.
		private string _proxyFile;		
		private string _assemblyName;			// BDS: Added this variable to hold the taget Assembly name for the generated files.		
		private SourceFiles _genFiles;			// BDS: Generated files collection.		
		private CodeDomProvider _codeGenerator;	// BDS: Code generator for this object instance.		
		private CodeGeneratorOptions _codeGenOptions;	// BDS: Code generation options for this instance.		
		private static string _implTypeName;	// BDS: Name of the service implementaion class.		
		private ArrayList _warnings;			// BDS: Added this warnings collection.
		private string _assemblyRootNamespace = "";
        private bool _changeCasing;
        private bool _enableWsdl;
        private string _relativeWsdlPath;
        private bool _enableDatabinding;
        private bool _orderIdentifiers;
        private bool _oldAsyncCode;
        private bool _noXxxSpecifiedHandling;
        private bool _useGenericList;
        private string _xsdoutputfilename;
	    private bool _removeDefaultValueAttribute;

	    // BDS: This enumeration is used to determine the source code file type 
		// when creating single files.
		private enum SourceCodeType
		{            
			ProxyCode, StubCode, AsmxDeclaration, AsmxImpl, Xsd,
		};
		
		public string AssemblyRootNamespace
		{
			get
			{
				return this._assemblyRootNamespace;
			}
			set
			{
				this._assemblyRootNamespace = value;
			}
		}

		// BDS: Commented this default ctor. 
		// Reason: The WebServiceCodeGenerator will not properly function without initializing 
		// the member fields.
		/*
		public WebServiceCodeGenerator()
		{
			// BDS: Init the source files container.
			_genFiles = new SourceFiles();
		}
		*/

		// BDS: Added the following new parameters. 
		// generateMultipleFiles: Specifies multiple file generation preference.
		// proxyName			: Specifies the file name for the proxy class.
		// stubName				: Specifies the file name for the stub class.
		// generateInterface	: Specifies the Interface generation preference.
		// generateHelp			: Specifies the Help file generation preference.
		public WebServiceCodeGenerator(string wsdlLocation,
			string lang,
			bool generateProps,
			string configFile,
			bool accessSoapMessages,
			bool endpointConfig,
			bool client,
			bool serializable,
			bool validation,
			bool collections,
			string wsdlContents,
			string serviceFile,
			bool generateMultipleFiles,
			string proxyName,
			string stubName,
			bool generateInterface,
			bool generateHelp,
			string assemblyName, 
            bool changeCasing, 
            bool enableWsdl, 
            string relativeWsdlPath, 
            bool enableDatabinding, 
            bool orderIdentifiers, 
            bool oldAsyncCode, 
            bool noXxxSpecifiedHandling, 
            bool useGenericList)
		{
			_wsdl = wsdlLocation;
			_wsdlSource = wsdlContents;
			_language = lang.ToLower();
			_genProps = generateProps;
			_appConfigFile = configFile;
			_accessSOAP = accessSoapMessages;
			_endpointURLConfig = endpointConfig;
			_clientSide = client;
			_serializableClasses = serializable;
			_enableValidation = validation;
			_generateCollections = collections;
			_serviceFile = serviceFile;			
			_generateMultipleFiles = generateMultipleFiles; // BDS: Initialize the multi-file generation option. 
			_proxyFile = proxyName;			// BDS: Initialize the file name for proxy.
			_stubFile = stubName;			// BDS: Initialize the file name for stub.
			_genFiles = new SourceFiles();	// BDS: Initialize the source files container.
			_implTypeName = string.Empty;	// BDS: Initialize the Impl type name.
			_generateInterface = generateInterface; // BDS: Initialize the Interface generation option.
			_generateHelp = generateHelp;	// BDS: Initialize the Help file generation option.
			_assemblyName = assemblyName;	// BDS: Initialize the assembly name.
            _changeCasing = changeCasing;
            _enableWsdl = enableWsdl;
            _relativeWsdlPath = relativeWsdlPath;
            _enableDatabinding = enableDatabinding;
            _orderIdentifiers = orderIdentifiers;
            _oldAsyncCode = oldAsyncCode;
            _noXxxSpecifiedHandling = noXxxSpecifiedHandling;
            _useGenericList = useGenericList;

			// BDS: Initialize the code generator.
			InitializeCodeGenerator();
		}

		// BDS: Added new parameter to obtain the target assembly name for the generated 
		// files.
		public WebServiceCodeGenerator(WebServicesCodeGenerationOptions options, 
			string assemblyName)
		{
			_wsdl = options.WsdlLocation;
			_wsdlSource = options.WsdlContents;
			_language = options.CodeLanguage.ToLower();
			_genProps = options.GenerateProperties;
			_appConfigFile = options.ConfigurationFile;
			_accessSOAP = options.EnableSoapMessagesAccess;
			_endpointURLConfig = options.EnableEndpointConfiguration;
			_clientSide = options.GenerateClientSideCode;
			_serializableClasses = options.GenerateSerializableTypes;
			_enableValidation = options.EnableValidation;
			_generateCollections = options.GenerateCollections;
			_serviceFile = options.WebServiceImplementationFile;
			_generateMultipleFiles = options.GenerateMultipleFiles; // BDS: Initialize the multi-file generation option. 
			_proxyFile = options.ProxyName;	// BDS: Initialize the file name for proxy.
			_stubFile = options.StubName;	// BDS: Initialize the file name for stub.	
			_genFiles = new SourceFiles();	// BDS: Initialize the source files container.
			_implTypeName = string.Empty;	// BDS: Initialize the Impl type name.
			_generateInterface = options.GenerateInterface; // BDS: Initialize the Interface generation option.
			_generateHelp = options.GenerateHelp; // BDS: Initialize the Help file generation option.
			_assemblyName = assemblyName;
            _changeCasing = options.ChangeCasing;
            _enableWsdl = options.EnableWsdl;
            _enableDatabinding = options.Databinding;
            _orderIdentifiers = options.OrderIdentifiers;
            _oldAsyncCode = options.OldAsyncCode;
            _noXxxSpecifiedHandling = options.NoXxxSpecifiedHandling;
            _useGenericList = options.UseGenericList;
		    _removeDefaultValueAttribute = options.RemoveDefaultValueAttribute;

			// BDS: Initialize the code generator.
			InitializeCodeGenerator();
		}

        // This is a lightweight ctor for XSD code generation.
        public WebServiceCodeGenerator(string language, 
                                        string xsdoutputfilename,
                                        bool generateproperties, 
                                        bool serializableclasses,
                                        bool collections, 
                                        bool genericlists, 
                                        bool enabledatabinding,
                                        bool generateorderidentifiers, 
                                        bool adjustcasing, 
                                        bool avoidxxxspecifiedhandling,
                                        bool separatefiles,
                                        bool removeDefaultValueAttribute)
        {
            this._language = language;
            this._xsdoutputfilename = xsdoutputfilename;
            this._genProps = generateproperties;
            this._serializableClasses = serializableclasses;
            this._generateCollections = collections;
            this._useGenericList = genericlists;
            this._enableDatabinding = enabledatabinding;
            this._orderIdentifiers = generateorderidentifiers;
            this._changeCasing = adjustcasing;
            this._noXxxSpecifiedHandling = avoidxxxspecifiedhandling;
            this._generateMultipleFiles = separatefiles;
            this._genFiles = new SourceFiles();
            this._removeDefaultValueAttribute = removeDefaultValueAttribute;
            InitializeCodeGenerator();
        }

		// BDS: Public interface to the warnings collection.
		public ArrayList Warnings
		{
			get
			{
				if(this._warnings == null)
				{
					this._warnings = new ArrayList();
				}
				return this._warnings;
			}
		}

		// BDS: Changed the return type to SourceFiles type.
		public SourceFiles GenerateProxyCode(string codeNamespace)
		{
			if(_wsdlSource.Length == 0) throw new CodeGenerationArgumentException("Parameter WsdlContents  needs to bet set to start code generation.");
			_genFiles.Clear();
			CheckBPCompliance();
			GenarateCommonCode(false,codeNamespace);
			return _genFiles;
		}

		// BDS: Changed the return type to SourceFiles type.
		public SourceFiles GenerateStubCode(string codeNamespace)
		{
			if(_wsdlSource.Length == 0) throw new CodeGenerationArgumentException("Parameter WsdlContents  needs to bet set to start code generation.");
			_genFiles.Clear();
			CheckBPCompliance();
			GenarateCommonCode(true,codeNamespace);
			GenerateStubOnlyCode(codeNamespace);

			// BDS: Create the source file object for the ASMX declarative and add it to the source files 
			// collection.
			SourceFile asmxdec = new SourceFile();
			asmxdec.AsmxDeclarative = true;
			asmxdec.SourceCode = GetCodeBanner() + GetASMXDeclarative();
			_genFiles.Add(asmxdec);
			
			return _genFiles;
		}

		private string GetASMXDeclarative()
		{
			string lang = "C#";
		
			if(_fullyQServiceClass.IndexOf(".") == 0)
			{
				_fullyQServiceClass = _fullyQServiceClass.Remove(0,1);
			}

			if (_language == "vb")
			{
				lang = "VB";
				if(this._assemblyRootNamespace.Trim() != "")
				{
					_fullyQServiceClass = string.Format(CultureInfo.CurrentCulture, @"{0}.{1}", 
						this._assemblyRootNamespace, _fullyQServiceClass);
				}
			}
	
			return string.Format(CultureInfo.CurrentCulture, @"<%@ WebService Language=""{0}""  Codebehind=""{1}"" Class=""{2}"" %>",
				lang, _serviceFile, _fullyQServiceClass);
		}

		
		private string GenerateASMXServerCode(ref CodeNamespace cns)
		{
			ArrayList codeTypesList = new ArrayList();

			foreach (CodeTypeDeclaration ctd in cns.Types)
			{
				if (ctd.BaseTypes.Count >0)
				{
					if (ctd.BaseTypes[0].BaseType != "System.Web.Services.WebService")
						codeTypesList.Add(ctd);
				}
				else
				{
					codeTypesList.Add(ctd);
				}
			}

			foreach (CodeTypeDeclaration ctd in codeTypesList)
			{
				cns.Types.Remove(ctd);
			}

			string className = "";
			
			
			foreach(CodeTypeDeclaration codeType in cns.Types)
			{
				// change the base class
				string xmlNamespace = "";
                string bindingName = codeType.Name;
				CodeTypeDeclaration ctDecl = codeType;
				cns.Types.Remove(ctDecl);
				className = ctDecl.Name;

				/// Change the type name only if we are implementing the abstract class.
				if(!_generateInterface) 
					ctDecl.Name = ctDecl.Name + "Impl";

				// BDS: Hold the name of the Impl type.
				_implTypeName = ctDecl.Name;

				// BDS: Add the interface to the interfaces list.
				if(_generateInterface)
					ctDecl.BaseTypes.Add(new CodeTypeReference("I"+ className));
				else
					ctDecl.BaseTypes[0] = new CodeTypeReference(className);

				ctDecl.Attributes = MemberAttributes.Public;
				ctDecl.TypeAttributes = TypeAttributes.Public | TypeAttributes.Class;
				
				// add the XML namespace attribute to the class
				foreach (CodeAttributeDeclaration codeAD in ctDecl.CustomAttributes)
				{
					if (codeAD.Name == "System.Web.Services.WebServiceBindingAttribute")
					{
                        bool isLocationFound = false;
						foreach (CodeAttributeArgument codeAA in codeAD.Arguments)
						{
							if (codeAA.Name == "Namespace")
							{
								xmlNamespace = ((CodePrimitiveExpression)codeAA.Value).Value.ToString();
							}

                            // BDS: Add this attribute only when neccessary.
                            if (_enableWsdl)
                            {
                                if (codeAA.Name == "Location")
                                {
                                    isLocationFound = true;
                                    codeAA.Value = new CodePrimitiveExpression(_relativeWsdlPath);
                                }
                            }

                            if (codeAA.Name == "Name")
                            {
                                bindingName = ((CodePrimitiveExpression)codeAA.Value).Value.ToString();
                            }
						}

                        // BDS: Add this attribute only when neccessary.
                        if (_enableWsdl)
                        {
                            if (!isLocationFound)
                            {
                                CodeAttributeArgument location = new CodeAttributeArgument();
                                location.Name = "Location";
                                location.Value = new CodePrimitiveExpression(_relativeWsdlPath);
                                codeAD.Arguments.Add(location);
                            }
                        }
					}
				}
				
				// BDS: Adding this attribute to RPC/Encoded style bindings causes an error. Therefore I removed
				// it for RPC/Encoded style bindings.
				// BDS: 11/25/2005: This attribute is now automatically added my .net fx code generator.
                /*
                if(CodeDecorator.namingConventionsApplicable)
				{
					CodeAttributeDeclaration cad = new CodeAttributeDeclaration("System.Web.Services.WebServiceAttribute");
					CodeAttributeArgument caa = new CodeAttributeArgument();
					CodePrimitiveExpression cpe = new CodePrimitiveExpression(xmlNamespace);

					caa.Name = "Namespace";
					caa.Value = cpe;
					cad.Arguments.Add(caa);				
					ctDecl.CustomAttributes.Add(cad);
				}

                */

				// BDS: This is no longer required.
				// CodeDecorator.UseCamelCaseParameterNames(ctDecl);
				cns.Types.Add(ctDecl);	// Add the modified class to the namespace

				className = ctDecl.Name;
				ArrayList codeMemberList = null;

				// get all the methods in the class and put it into a list
				foreach (CodeTypeMember codeMember in codeType.Members)
				{
					CodeMemberMethod codeMethod = codeMember as CodeMemberMethod;

					if (codeMethod == null)
						continue;

					if ((codeMethod.Attributes & MemberAttributes.Public) == MemberAttributes.Public)
					{
						if (codeMemberList == null)
							codeMemberList = new ArrayList(codeType.Members.Count);

						codeMemberList.Add(codeMethod);
					}
				}

				if (codeMemberList == null)
					break;
				
				foreach (CodeMemberMethod codeMethod in codeMemberList)
				{
					// BDS: Dont make the members override if the type implements the interface.
					if(!_generateInterface)
						codeMethod.Attributes = MemberAttributes.Public |
							MemberAttributes.Override;
					else
					{
						codeMethod.Attributes = MemberAttributes.Public;
						codeMethod.ImplementationTypes.Add("I"+ className);
					}

					// BDS: Commented the following two lines as they are no longer in use.
					// Type returnType = Type.GetType(codeMethod.ReturnType.BaseType);
					// CodeMethodReturnStatement cmrs;
					
					//
					// BDS: Add the statement to throw NotImplementedException exception.
					//
					CodeThrowExceptionStatement niexception = new CodeThrowExceptionStatement(
						new CodeObjectCreateExpression(
						new CodeTypeReference(typeof(System.NotImplementedException)),
						new CodePrimitiveExpression[] {}));

					codeMethod.Statements.Add(niexception);
					//
					// End of throw NotImplementedException statement.
					//		
			        
                    // Add the binding argument to the SoapDocumentMethodAttribute.                    
                    bool bindingFound = false;
                    foreach (CodeAttributeDeclaration methodAttribute in codeMethod.CustomAttributes)
                    {
                        if (methodAttribute.AttributeType.BaseType ==
                            @"System.Web.Services.Protocols.SoapDocumentMethodAttribute" || 
                            methodAttribute.AttributeType.BaseType ==
                            @"System.Web.Services.Protocols.SoapRpcMethodAttribute")
                        {
                            foreach (CodeAttributeArgument arg in methodAttribute.Arguments)
                            {
                                if (arg.Name == "Binding")
                                {
                                    arg.Value = new CodePrimitiveExpression(bindingName);
                                    bindingFound = true;
                                    break;
                                }
                            }

                            if (!bindingFound)
                            {
                                CodeAttributeArgument bindingArg = new CodeAttributeArgument();
                                bindingArg.Name = "Binding";
                                bindingArg.Value = new CodePrimitiveExpression(bindingName);
                                methodAttribute.Arguments.Add(bindingArg);
                                bindingFound = true;
                            }

                            break;
                        }
                    }

                    if (!bindingFound)
                    {
                        CodeAttributeDeclaration soapDocAttribute =
                            new CodeAttributeDeclaration(
                            new CodeTypeReference(typeof(SoapDocumentMethodAttribute)));
                        CodeAttributeArgument bindingArg = new CodeAttributeArgument();
                        bindingArg.Name = "Binding";
                        bindingArg.Value = new CodePrimitiveExpression(bindingName);
                        soapDocAttribute.Arguments.Add(bindingArg);
                        codeMethod.CustomAttributes.Add(soapDocAttribute);
                    }
                    
				}
				break;
			}

			return className;
		}

		private string CheckForImports(string baseWSDLUrl, string serviceName)
		{	
			string newServiceName = serviceName;
			DiscoveryClientProtocol dcp = new DiscoveryClientProtocol();
			dcp.AllowAutoRedirect = true;
			dcp.Credentials = CredentialCache.DefaultCredentials;

			dcp.DiscoverAny(baseWSDLUrl);
			dcp.ResolveAll();            
			foreach (object osd in dcp.Documents.Values)
			{
				if (osd is ServiceDescription)
				{
					if (_endpointURLConfig)
					{
						// BDS: Modified this section to read the service name from all possible places
						// in the WSDL.
						if (((ServiceDescription)osd).Services.Count > 0)
						{
							newServiceName = ((ServiceDescription)osd).Services[0].Name + ".ServiceEndpointURL";
						}
						else if(((ServiceDescription)osd).Name != null && 
							((ServiceDescription)osd).Name != "")
						{
							newServiceName = ((ServiceDescription)osd).Name + ".ServiceEndpointURL";
						}
						else if(((ServiceDescription)osd).Bindings.Count > 0)
						{
							newServiceName = ((ServiceDescription)osd).Bindings[0].Name + ".ServiceEndpointURL";
						}
					}

					sdi.AddServiceDescription((ServiceDescription)osd, newServiceName, "");                    
				}

				if (osd is XmlSchema)
				{
					sdi.Schemas.Add((XmlSchema)osd);                    
				}                
			}
			return newServiceName;
		}

		private string GetCodeBanner()
		{
			string banner, version;
			version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
			
			// BDS: Added file time to the file header.
			if(_language == "cs")
			{
				banner = 
                    "#region WSCF\r\n" +
					"//------------------------------------------------------------------------------\r\n" + 
					"// <autogenerated code>\r\n" +
					"//     This code was generated by a tool.\r\n" +
					"//     Changes to this file may cause incorrect behavior and will be lost if \r\n" +
					"//     the code is regenerated.\r\n" +
					"// </autogenerated code>\r\n" +
					"//------------------------------------------------------------------------------\r\n" +
					"// File time " + DateTime.Now.ToString("dd-MM-yy hh:mm tt") + "\r\n" + 
					"//\r\n" +
					"// This source code was auto-generated by WsContractFirst, Version=" + version + "\r\n" + 
                    "#endregion";
			}
			else
			{

				banner = 
                    "#Region \"WSCF\"\r\n" +
					"'------------------------------------------------------------------------------\r\n" + 
					"' <autogenerated code>\r\n" +
					"'     This code was generated by a tool.\r\n" +
					"'     Changes to this file may cause incorrect behavior and will be lost if \r\n" +
					"'     the code is regenerated.\r\n" +
					"' </autogenerated code>\r\n" +
					"'------------------------------------------------------------------------------\r\n" +
					"' File time " + DateTime.Now.ToString("dd-MM-yy hh:mm tt") + "\r\n" + 
					"'\r\n" +
                    "' This source code was auto-generated by WsContractFirst, Version=" + version + "\r\n" + 
                    "#End Region";
			}
			
			banner += "\r\n\r\n\r\n";
			return banner;
		}
		
		/// <summary>
		/// This procedure genrates the SourceFile objects common to both stub and 
		/// proxy classes.
		/// </summary>
		/// <param name="service">Indicates whether to import proxy or stub from WSDL.</param>
		/// <param name="codeNamespace">Name of the code namespace.</param>
		/// <author>BDS - thinktecture</author>
		private void GenarateCommonCode(bool service, string codeNamespace)
		{
			// Validate the parameters.
			Debug.Assert((codeNamespace != null),"Parameter codeNamespace could not be null");
			//Debug.Assert((codeNamespace.Length != 0),"Parameter codeNamespace could not be a 0 length string.");

			// Use an XmlTextReader to get the Web Service description
			StringReader  wsdlStringReader = new StringReader(_wsdlSource);
			XmlTextReader tr = new XmlTextReader(wsdlStringReader);
            
			try
			{
				ServiceDescription.Read(tr);
			}
			catch(Exception ex)
			{
				throw new WsdlProcessingException("An error occured while processing WSDL.", ex);
			}

			tr.Close();

			// WSDL service description importer 
			CodeNamespace cns = new CodeNamespace(codeNamespace);
			
			sdi = new ServiceDescriptionImporter();
			if(service) sdi.Style = ServiceDescriptionImportStyle.Server;
			
			string serviceEndpoint = "";
			string serviceName = "";

			if (_endpointURLConfig)
			{
				if(_clientSide)
					serviceName = ConfigHelper.GetLastServiceEndpointName(_appConfigFile + @"\App.config");
				else
					serviceName = ConfigHelper.GetLastServiceEndpointName(_appConfigFile + @"\Web.config");
			}

			string newServiceName = CheckForImports(_wsdl, serviceName);
			if(newServiceName != serviceName) serviceName = newServiceName;

			// TODO: check for all bindings and their appropriate location value

			// CW: Fixed a bug here. We did not check whether there are any non-SOAP bindings
			// Maybe we need to add support for non-SOAP bindings some day.
			// BDS: Fixed a bug here. We checked only the first binding extenstion. But there could be multiple 
			// bindings and the SOAP binding could be the last one in the collection.
			if (sdi.ServiceDescriptions[0].Services.Count > 0)
			{
				bool soapBindingFound = false;
				foreach(Port p in sdi.ServiceDescriptions[0].Services[0].Ports)
				{					
					foreach(ServiceDescriptionFormatExtension extension in p.Extensions)
					{
						SoapAddressBinding sab = extension as SoapAddressBinding;
						if(sab != null)
						{
							serviceEndpoint = sab.Location;
							soapBindingFound = true;
							goto EndBindingCheck;
						}												
					}					
				}
			
				
				if(!soapBindingFound)
				{
					throw new Exception("Sorry. WSCF currently only supports SOAP bindings.");
				}
				
				EndBindingCheck:;
			}

			if (_endpointURLConfig)
			{
				if(_clientSide)
					ConfigHelper.SetAppSettingsValue(_appConfigFile + @"\App.config", serviceName, serviceEndpoint);
				else
					ConfigHelper.SetAppSettingsValue(_appConfigFile + @"\Web.config", serviceName, serviceEndpoint);
			}

			sdi.ProtocolName = "Soap";

            if(_enableDatabinding)
            {
                sdi.CodeGenerationOptions |= CodeGenerationOptions.EnableDataBinding;
			}

            if (_orderIdentifiers)
            {
                sdi.CodeGenerationOptions |= CodeGenerationOptions.GenerateOrder;
            }

            if (_oldAsyncCode)
            {
                sdi.CodeGenerationOptions |= CodeGenerationOptions.GenerateOldAsync;
            }
            else
            {
                // Remove old async option if it is there by default.
                if ((sdi.CodeGenerationOptions & CodeGenerationOptions.GenerateOldAsync) == CodeGenerationOptions.GenerateOldAsync)
                {
                    sdi.CodeGenerationOptions ^= CodeGenerationOptions.GenerateOldAsync;
                }

                sdi.CodeGenerationOptions |= CodeGenerationOptions.GenerateNewAsync;
            }
            
            ServiceDescriptionImportWarnings warning = sdi.Import(cns, null);
            string warningString;

            if (warning != 0)
            {
                switch (warning)
                {
                    case ServiceDescriptionImportWarnings.NoCodeGenerated:
                        warningString = "No code generated";
                        break;
                    case ServiceDescriptionImportWarnings.NoMethodsGenerated:
                        warningString = "No methods generated";
                        break;
                    case ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored:
                        warningString = "Unsupported operations ignored";
                        break;
                    case ServiceDescriptionImportWarnings.OptionalExtensionsIgnored:
                        warningString = "Optional extensions ignored";
                        break;
                    case ServiceDescriptionImportWarnings.RequiredExtensionsIgnored:
                        warningString = "Required extensions ignored";
                        break;
                    case ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored:
                        warningString = "Unsupported bindings ignored";
                        break;
                    default:
                        warningString = "General warning";
                        break;
                }

                AddWarning(warningString);
            }

			CodeDecorator.namingConventionsApplicable = !WsdlHelper.HasRpcEncodedBindings(_wsdlSource);
			
			if(CodeDecorator.namingConventionsApplicable)
			{
				CodeDecorator.EmitXmlElementAttribute(cns);
				CodeDecorator.GenerateArrayWrappers(cns, sdi);
			}
			if (_generateCollections)
			{
				CodeDecorator.ChangeArraysToCollections(ref cns, false);
			}
            else if (_useGenericList)
            {
                CodeDecorator.ChangeArraysToGenericList(ref cns, false);
            }

			if(CodeDecorator.namingConventionsApplicable)
			{
                if (_changeCasing)
                {
                    CodeDecorator.UsePascalCase(cns);
                }
				CodeDecorator.UseCamelCaseParameterNames(cns);
			}
			
			if (_accessSOAP)
			{	
				// TODO: loop through all bindings and change base class of each binding

				// change the base class
				CodeTypeDeclaration ctDecl = cns.Types[0];
				cns.Types.Remove(ctDecl);
				
				ctDecl.BaseTypes[0] = new CodeTypeReference("Thinktecture.Tools.Web.Services.Extensions.SoapHttpClientProtocolExtended");
				cns.Types.Add(ctDecl);

				// add configuration details
				if (_clientSide)
					ConfigHelper.SetWebServicesPipelineConfig(_appConfigFile + @"\App.config", "Thinktecture.Tools.Web.Services.Extensions.SoapMessageAccessClientExtension, Thinktecture.Tools.Web.Services.Extensions.Messages");
				else
					ConfigHelper.SetWebServicesPipelineConfig(_appConfigFile + @"\Web.config", "Thinktecture.Tools.Web.Services.Extensions.SoapMessageAccessClientExtension, Thinktecture.Tools.Web.Services.Extensions.Messages");
			}			

			//
			// BDS: Generate the proxy and stub interfaces.
			//
			if(_generateInterface)
			{
				int index = 0;
				while(index < cns.Types.Count)
				{
					CodeTypeDeclaration typeDcl = cns.Types[index];
					
					if((typeDcl.BaseTypes.Count > 0) &&	
						((typeDcl.TypeAttributes & TypeAttributes.Abstract) == TypeAttributes.Abstract) &&
						(typeDcl.BaseTypes[0].BaseType == "System.Web.Services.WebService"))
					{
						GenerateInterfaceForType(typeDcl,cns);		// Generate the interface for the type.
						cns.Types.Remove(typeDcl);					// Remove the abstract class from the namespace.
						continue;	// Goto the next item with out incrementing the index.
					}
					else if((typeDcl.BaseTypes.Count > 0) && 
						((typeDcl.BaseTypes[0].BaseType == "System.Web.Services.Protocols.SoapHttpClientProtocol")
						|| (typeDcl.BaseTypes[0].BaseType == "Thinktecture.Tools.Web.Services.Extensions.SoapHttpClientProtocolExtended")))
					{	
						// Generate the interface and 
						// set the base interface for the proxy class.
						string typeInterface = GenerateInterfaceForType(typeDcl,cns);
						typeDcl.BaseTypes.Add(typeInterface);
											
						// Explicitly set the method implementation.
						// If URL configuration is required, modify the proxy contructor code to throw an 						
						// exception when the URL endpoint is either empty or not specified in the config file. 
						// BDS: Later removed the exception throwing code snippet as some devs may need to
						// set the URL by using the URL property.
						foreach(CodeTypeMember member in typeDcl.Members)
						{
							CodeMemberMethod method = member as CodeMemberMethod;
							if(method != null)
							{
                                // BDS: Modified the logic we use to check the async method. 
                                // Now we just check the number of attributes in the custom attributes collection.
								if(method.CustomAttributes.Count > 0)
								{									
									method.ImplementationTypes.Add(typeInterface);
								}

								// Throw an exception when the endpoint URL is either empty or not specified in the 
								// config file.
								if(_endpointURLConfig)
								{
									if(method is CodeConstructor)
									{
										CodeConstructor ctor = method as CodeConstructor;
								
										foreach(CodeStatement statement in ctor.Statements)
										{
											CodeConditionStatement ifcon = statement as CodeConditionStatement;
									
											if(ifcon != null)
											{
												// Extend the condition to check whether the URL string is empty or not.
												CodeBinaryOperatorExpression condition1 = 
													new CodeBinaryOperatorExpression(
													new CodeVariableReferenceExpression("urlSetting"),
													CodeBinaryOperatorType.IdentityInequality, 
													new CodePrimitiveExpression(string.Empty));
												CodeBinaryOperatorExpression condition2 = 
													new CodeBinaryOperatorExpression(ifcon.Condition, 
													CodeBinaryOperatorType.BooleanAnd, 
													condition1);
												ifcon.Condition = condition2;																			
											}
										}
									}
								}
							}
						}						
					}

					// Increment the index.
					index++;
				}
			}
			//
			// BDS: End of interface conversion
			//

			if (_genProps)
			{
				CodeDecorator.ChangeFieldsToProperties(ref cns, this._language, this._enableDatabinding, null, _noXxxSpecifiedHandling);
			}

			if (!_serializableClasses)
				CodeDecorator.RemoveSerializableAttribute(ref cns);			

			CodeDecorator.EmitCodeComments(sdi, ref cns);
			CodeDecorator.ImportDocumentation(cns,sdi);			
			CodeDecorator.AddCtors(cns);
			
			// BDS: Introducing the Multiple file generation option.
			if(_generateMultipleFiles)
			{
				GenerateMultipleFiles(cns);
			}
			else
			{
				if(service)
				{
					GenerateSingleFile(cns, SourceCodeType.StubCode);
				}
				else
				{
					GenerateSingleFile(cns, SourceCodeType.ProxyCode);
				}
			}            
		}
		
		
		/// <summary>
		/// This procedure genrates the SourceFile objects specific to stub class. 
		/// </summary>
		/// <param name="codeNamespace">Name of the code namespace.</param>
		private void GenerateStubOnlyCode(string codeNamespace)
		{
			// Validate the parameters.
			Debug.Assert((codeNamespace != null),"Parameter codeNamespace could not be null");
			Debug.Assert((codeNamespace.Length != 0),"Parameter codeNamespace could not be a 0 length string.");

			// Use an XmlTextReader to get the Web Service description
			StringReader  wsdlStringReader = new StringReader(_wsdlSource);
			XmlTextReader tr = new XmlTextReader(wsdlStringReader);

			try
			{
				ServiceDescription.Read(tr);
			}
			catch(Exception ex)
			{
				throw new WsdlProcessingException("An error occured while processing WSDL.", ex);
			}

			tr.Close();

			// WSDL service description importer 
			CodeNamespace cns = new CodeNamespace(codeNamespace);
			
			sdi = new ServiceDescriptionImporter();
			
			string serviceEndpoint = "";
			string serviceName = "";

			string newServiceName = CheckForImports(_wsdl, serviceName);
			if(newServiceName != serviceName) serviceName = newServiceName;

			// TODO: check for all bindings and their appropriate location value

			if (sdi.ServiceDescriptions[0].Services.Count > 0)
				serviceEndpoint= 
					((SoapAddressBinding)sdi.ServiceDescriptions[0].Services[0].Ports[0].Extensions[0]).Location;

			sdi.ProtocolName = "Soap";
			sdi.Style = ServiceDescriptionImportStyle.Server;
			sdi.Import(cns, null);

			if(CodeDecorator.namingConventionsApplicable)
			{
				CodeDecorator.EmitXmlElementAttribute(cns);
				CodeDecorator.GenerateArrayWrappers(cns, sdi);
			}	

			if (_generateCollections)
			{
				CodeDecorator.ChangeArraysToCollections(ref cns, true);
			}	
            else if(_useGenericList)
            {
                CodeDecorator.ChangeArraysToGenericList(ref cns, true);
            }

			if(CodeDecorator.namingConventionsApplicable)
			{
                if (_changeCasing)
                {
                    CodeDecorator.UsePascalCase(cns);
                }
				CodeDecorator.UseCamelCaseParameterNames(cns);
			}

			// BDS:Import the System.Web namespace.
			// Reason: This is useful some automatically generated classes. (i.e Custom http module classes)
			cns.Imports.Add(new CodeNamespaceImport("System.Web"));			
			
            // Backup the original code namespace.
            CodeNamespace cnsOriginal = new CodeNamespace();
            CodeTypeDeclaration[] allTypes = new CodeTypeDeclaration[cns.Types.Count];
            cns.Types.CopyTo(allTypes, 0);
            cnsOriginal.Types.AddRange(allTypes);

			_fullyQServiceClass = cns.Name + "." + GenerateASMXServerCode(ref cns);

			if(_enableValidation)
			{
				ConfigHelper.SetWebServicesPipelineConfig(_appConfigFile + @"\Web.config", "Thinktecture.Tools.Web.Services.Extensions.ValidationExtension,Thinktecture.Tools.Web.Services.Extensions.Validation");
				string serviceAsmxFile = _serviceFile;
				int charIndex = serviceAsmxFile.LastIndexOf(".");
				serviceAsmxFile = serviceAsmxFile.Substring(0, charIndex);
				// BDS: Modified the code to use the relative path to the wsdl in the config file.
				//string relativeWsdlPath = 
				//	FileManipulationHelper.GetRelativePath(this._wsdl, this._appConfigFile);
                string wsdlLocationKey = _stubFile + ".asmx_Wsdl";
				ConfigHelper.SetAppSettingsValue(
					_appConfigFile + @"\Web.config", wsdlLocationKey, _relativeWsdlPath);				
			}								

			if (_genProps)
			{
				CodeDecorator.ChangeFieldsToProperties(ref cns, this._language, false, cnsOriginal, _noXxxSpecifiedHandling);
			}

			if (!_serializableClasses)
				CodeDecorator.RemoveSerializableAttribute(ref cns);
						
			CodeDecorator.EmitCodeComments(sdi, ref cns);				
			CodeDecorator.AddCtors(cns);
			
			// BDS: Generate the help files if requested.
			if(_generateHelp)
			{
				GenerateHelpFiles();
			}
			else
			{
				// BDS: Disable wsdl is done only when the help files are not generated.
				// Reason: Helpfile generation will automatically block the requests for 
				// wsdl.
				ConfigHelper.DisableWsdlConfig(_appConfigFile + @"\Web.config");
			}

            // Enable/Disable SOAP 1.2 protocol.
            AddRemoveSoap12Protocol(_appConfigFile + @"\Web.config");

			// BDS: Generate multiple files if requested.
			if(_generateMultipleFiles)
				GenerateMultipleFiles(cns);
			else
				GenerateSingleFile(cns, SourceCodeType.AsmxImpl);
		}
        
        /// <summary>
        /// Generates the data contracts for given xsd file(s).
        /// </summary>        
        public SourceFiles GenerateDataContracts(string[] xsdfiles, string ns)
        {
            // Do the basic validations of the input files
            if (xsdfiles == null)
                throw new ArgumentNullException("xsdfiles");
            if (ns == null)
                throw new ArgumentNullException("ns");
            if (ns.Trim() == "")
                throw new ArgumentException("ns could not be an empty string.");            

            // Resolve the schemas.
            XmlSchemas schemas = new XmlSchemas();
            for (int fi = 0; fi < xsdfiles.Length; fi++)
            {
                // Skip the non xsd/wsdl files.
                string lowext = Path.GetExtension(xsdfiles[fi]).ToLower();
                if (lowext == ".xsd") // This is an XSD file.
                {
                    XmlTextReader xmltextreader = null;

                    try
                    {
                        xmltextreader = new XmlTextReader(xsdfiles[fi]);
                        XmlSchema schema = XmlSchema.Read(xmltextreader, null);
                        XmlSchemaSet schemaset = new XmlSchemaSet();
                        schemaset.Add(schema);
                        RemoveDuplicates(ref schemaset);
                        schemaset.Compile();
                        schemas.Add(schema);
                    }
                    finally
                    {
                        if (xmltextreader != null)
                        {
                            xmltextreader.Close();
                        }
                    }
                }
                else if (lowext == ".wsdl") // This is a WSDL file.
                {
                    XmlSchemaSet schemaset = new XmlSchemaSet();
                    XmlSchemaSet embeddedschemaset = new XmlSchemaSet();
                    DiscoveryClientProtocol dcp = new DiscoveryClientProtocol();
                    dcp.AllowAutoRedirect = true;
                    dcp.Credentials = CredentialCache.DefaultCredentials;
                    dcp.DiscoverAny(xsdfiles[fi]);
                    dcp.ResolveAll();
                    foreach (object document in dcp.Documents.Values)
                    {
                        if (document is XmlSchema)
                        {
                            schemaset.Add((XmlSchema)document);
                            schemas.Add((XmlSchema)document);
                        }
                        if (document is ServiceDescription)
                        {
                            schemas.Add(((ServiceDescription)document).Types.Schemas);
                            foreach (XmlSchema schema in ((ServiceDescription)document).Types.Schemas)
                            {
                                embeddedschemaset.Add(schema);
                                schemas.Add(schema);
                            }
                        }
                    }
                    RemoveDuplicates(ref schemaset);
                    schemaset.Add(embeddedschemaset);
                    schemaset.Compile();
                }                                
            }
                       
            CodeCompileUnit cpunit = new CodeCompileUnit();
            CodeNamespace cns = new CodeNamespace(ns);
            cpunit.Namespaces.Add(cns);

            CodeDomProvider codeprovider = null;
            if (_language == "vb")
                codeprovider = new VBCodeProvider();
            else if (_language == "cs")
                codeprovider = new CSharpCodeProvider();
            
            // Build the code generation options.
            CodeGenerationOptions options = CodeGenerationOptions.None;
            if (_enableDatabinding)            
                options |= CodeGenerationOptions.EnableDataBinding;            
            if (_orderIdentifiers)            
                options |= CodeGenerationOptions.GenerateOrder;            

            // Build the CodeDom object graph.
            XmlCodeExporter codeexporter = new XmlCodeExporter(cns, cpunit, options, null);
            CodeIdentifiers cis = new CodeIdentifiers();
            ImportContext impcontext = new ImportContext(cis, false);
            XmlSchemaImporter schemaimporter = new XmlSchemaImporter(schemas, options,
                                                                    codeprovider, impcontext);
            for (int si = 0; si < schemas.Count; si++)
            {
                XmlSchema schema = schemas[si];
                IEnumerator enumerator = schema.Elements.Values.GetEnumerator();
                IEnumerator enumerator2 = schema.SchemaTypes.Values.GetEnumerator();
                try
                {
                    while (enumerator.MoveNext())
                    {
                        XmlSchemaElement element = (XmlSchemaElement)enumerator.Current;
                        if (!element.IsAbstract)
                        {
                            XmlTypeMapping typemapping = schemaimporter.ImportTypeMapping(element.QualifiedName);
                            codeexporter.ExportTypeMapping(typemapping);
                        }
                    }
                    while (enumerator2.MoveNext())
                    {
                        XmlSchemaType type = (XmlSchemaType)enumerator2.Current;
                        if (!CouldBeAnArray(type))
                        {
                            XmlTypeMapping typemapping = schemaimporter.ImportSchemaType(type.QualifiedName);
                            codeexporter.ExportTypeMapping(typemapping);
                        }                        
                    }
                }
                finally
                {
                    IDisposable disposableobject = enumerator as IDisposable;
                    if (disposableobject != null)
                        disposableobject.Dispose();
                    IDisposable disposableobject2 = enumerator2 as IDisposable;
                    if (disposableobject2 != null)
                        disposableobject2.Dispose();
                }
            }
            if (cns.Types.Count == 0)
                throw new CodeGenerationException("No types were generated.");

            // Apply the code decorations.
            CodeDecorator.EmitXmlElementAttribute(cns);
            if (_removeDefaultValueAttribute)
                CodeDecorator.RemoveDefaultValueAttributes(cns);
            if (_generateCollections)            
                CodeDecorator.ChangeArraysToCollections(ref cns, false);            
            else if (_useGenericList)            
                CodeDecorator.ChangeArraysToGenericList(ref cns, false);

            if (_changeCasing)
            {
                CodeDecorator.UsePascalCase(cns);
                CodeDecorator.UseCamelCaseParameterNames(cns);
            }

            if (_genProps)            
                CodeDecorator.ChangeFieldsToProperties(ref cns, this._language, this._enableDatabinding, null, _noXxxSpecifiedHandling);
            if (!_serializableClasses)
                CodeDecorator.RemoveSerializableAttribute(ref cns);            
            CodeDecorator.AddCtors(cns);

            if (_generateMultipleFiles)
                GenerateMultipleFiles(cns);
            else
                GenerateSingleFile(cns, SourceCodeType.Xsd);

            return _genFiles;         
        }

		/// <summary>
		/// This metod creates a SourceFile object for each Type found in the CodeNameSpace spcified by pns parameter.
		/// </summary>
		/// <param name="pns">Code namespace.</param>
		/// <param name="pcgen">Code generator object to generate the code.</param>		
		/// <returns>A SourceFiles container with all the created SourceFile objects.</returns>
		/// <author>BDS - thinktecture</author>
        private void GenerateMultipleFiles(CodeNamespace pns)
        {
            // Validate the parameters. 
            // BDS: I have changed the throw statemet to debug assert statement as this is a private method.
            Debug.Assert((pns != null), "Parameter pns could not be null");

            CodeNamespace tempns = new CodeNamespace(pns.Name);

            // Copy imports one by one. 
            // BDS: we have to do this since "Imports" collection does not have the 
            // support for AddRange() method.
            foreach (CodeNamespaceImport import in pns.Imports)
            {
                tempns.Imports.Add(import);
            }
            tempns.Comments.AddRange(pns.Comments);

            string fname = string.Empty;

            foreach (CodeTypeDeclaration type in pns.Types)
            {
                //
                // Determine the file name to be used.
                // Logic: 
                // If the type inherits "System.Web.Services.WebService" then it's a stub file.
                // If the type inherits "System.Web.Services.Protocols.SoapHttpClientProtocol" or 
                // "Thinktecture.Tools.Web.Services.Extensions.SoapHttpClientProtocolExtended" then it's a 
                // proxy file. 
                // Otherwise the type is someother complex type (i.e. Employee). 
                // So use the Type name as the file name. 
                // 
                SourceFile sf = new SourceFile();
                if ((type.BaseTypes.Count > 0) &&
                    (type.BaseTypes[0].BaseType == "System.Web.Services.WebService"))
                {
                    fname = _stubFile + "." + _language;
                }
                else if ((type.BaseTypes.Count > 0) &&
                    ((type.BaseTypes[0].BaseType == "System.Web.Services.Protocols.SoapHttpClientProtocol")
                    || (type.BaseTypes[0].BaseType ==
                    "Thinktecture.Tools.Web.Services.Extensions.SoapHttpClientProtocolExtended")))
                {
                    fname = _proxyFile + "." + _language;
                    // BDS 09-19-2005 
                    // Set the ProxyImplementation property to true. Eventhough this is not used in VS addin
                    // command line tool uses this property to determine the type of the code file. 
                    sf.ProxyImplementation = true;
                }
                else
                {
                    fname = type.Name + "." + _language;
                }

                tempns.Types.Clear();	// Clear the exsisting items on the namespace.
                tempns.Types.Add(type);
                sf.FileName = fname;
                sf.SourceCode = GenerateCodeFromNamespace(tempns); ;

                if (_implTypeName == type.Name)
                    sf.ServiceImplementation = true;

                _genFiles.Add(sf);
            }
        }

		/// <summary>
		/// This method creates a single file for all types found in the namespace specified.
		/// </summary>
		/// <param name="pns">CodeDOM namespace for code generation.</param>
		/// <param name="pcgen">Reference to an instance of a code generator.</param>
		/// <param name="pst">Type of the source code file.</param>
		/// <author>BSD - thinktecture</author>
		private void GenerateSingleFile(CodeNamespace pns, SourceCodeType pst)
		{
			// Validate the parameters. 
			// BDS: I have changed the throw statemet to debug assert statement as this is a private method.
			Debug.Assert((pns != null),"Parameter pns could not be null");
			
			SourceFile sf = new SourceFile();
			// Select the file name.
			switch(pst)
			{
				case SourceCodeType.ProxyCode:
					sf.FileName = _proxyFile;
					sf.ProxyImplementation = true;
					break;
				case SourceCodeType.StubCode:
					// Use the service description name as the file name.
					// BDS: Fixed a bug here: Removed not supported charactors from the file name.
					// Also changed the code to use the common function in WsdlHelper to obtain the 
					// file name.
					string stubFileName = "";
					stubFileName = WsdlHelper.GetWsdlName(this._wsdlSource);

					// Remove the invalid chars.
					stubFileName = stubFileName.Replace("\\", "");
					stubFileName = stubFileName.Replace("/", "");
					stubFileName = stubFileName.Replace(":", "");
					stubFileName = stubFileName.Replace("*", "");
					stubFileName = stubFileName.Replace("?", "");
					stubFileName = stubFileName.Replace("\"", "");
					stubFileName = stubFileName.Replace("<", "");
					stubFileName = stubFileName.Replace(">", "");
					stubFileName = stubFileName.Replace("|", "");
					
					if(stubFileName == "")
					{
						stubFileName = "WebService";
					}

					stubFileName += "Types." + _language;					
					sf.FileName = stubFileName;
					break;
				case SourceCodeType.AsmxDeclaration:
					sf.AsmxDeclarative = true;
					break;
				case SourceCodeType.AsmxImpl:
					sf.ServiceImplementation = true;
					break;
                case SourceCodeType.Xsd:
                    sf.FileName = _xsdoutputfilename;
                    break;

			}
			sf.SourceCode = GenerateCodeFromNamespace(pns);
			_genFiles.Add(sf);
		}
	
		private void GenerateHelpFiles()
		{
			// Obtain the code for the ASPX page and construct a SourceFile object.
			ResourceManager rm = new ResourceManager(
                "Thinktecture.Tools.Web.Services.CodeGeneration.Resources.CodeGenerationResources",
				Assembly.GetExecutingAssembly());
			
			SourceFile aspx = new SourceFile();
			aspx.FileName = "AsmxHelpPage.aspx";
			aspx.AspxHelpPage = true;
            aspx.SourceCode = rm.GetString("AsmxHelpPage");

            // Add the link to the WSDL document if the WSDL feature is enabled.
            if (_enableWsdl)
            {
                string enableWsdlStatement = rm.GetString("EnableWsdl");
                aspx.SourceCode = aspx.SourceCode.Replace("@@WSDLLINK@@", enableWsdlStatement);
            }
            else
            {
                aspx.SourceCode = aspx.SourceCode.Replace("@@WSDLLINK@@", "");
            }
			
			// Add the aspx file to the source files collection.
			_genFiles.Add(aspx);

            // Cerate the new Http Module to handle the asmx request.
            CodeTypeDeclaration asmxHelpModule = new CodeTypeDeclaration("AsmxHelpModule");
            asmxHelpModule.IsClass = true;
            asmxHelpModule.TypeAttributes = TypeAttributes.Public;

            // Add the base interface.
            // BDS: Added this hack to make the VB code to use implements key word when adding the 
            // IHttpModule.If we have only one base type the generated code will use the Inherits 
            // keyword.
            if (this._language.ToLower() == "vb")
            {
                asmxHelpModule.BaseTypes.Add(typeof(object));
            }
            asmxHelpModule.BaseTypes.Add("System.Web.IHttpModule");

            // Create the ctor.
            asmxHelpModule.Members.Add(new CodeConstructor());

            // Add the Application_BeginRequest() handler.
            CodeMemberMethod appBeginRequest = new CodeMemberMethod();
            appBeginRequest.Name = "Application_BeginRequest";
            appBeginRequest.Attributes = MemberAttributes.Private;
            appBeginRequest.ReturnType = new CodeTypeReference(typeof(void));
            appBeginRequest.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "sender"));
            appBeginRequest.Parameters.Add(new CodeParameterDeclarationExpression(typeof(EventArgs), "e"));

            // Create and add the statements in the method body.
            CodeVariableDeclarationStatement requestPath = new CodeVariableDeclarationStatement(
                typeof(string), "requestPath");
            
            requestPath.InitExpression = new CodePropertyReferenceExpression(
                new CodeTypeReferenceExpression("HttpContext.Current.Request"),
                "RawUrl");
            
            CodeVariableReferenceExpression requestPathRef =
                new CodeVariableReferenceExpression("requestPath");
            
            CodeAssignStatement trim = new CodeAssignStatement(requestPathRef,
                new CodeMethodInvokeExpression(requestPathRef, "Trim"));
            
            CodeAssignStatement toLower = new CodeAssignStatement(requestPathRef,
                new CodeMethodInvokeExpression(requestPathRef, "ToLower"));
            
            CodeVariableDeclarationStatement setting = new CodeVariableDeclarationStatement(
                typeof(string), "setting");

            setting.InitExpression = new CodeIndexerExpression(
                new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("ConfigurationManager"),
                "AppSettings"), new CodePrimitiveExpression("disableWsdl"));


            appBeginRequest.Statements.Add(requestPath);
            appBeginRequest.Statements.Add(trim);
            appBeginRequest.Statements.Add(toLower);
            appBeginRequest.Statements.Add(setting);

            CodeVariableReferenceExpression settingRef = new CodeVariableReferenceExpression("setting");

            CodeBinaryOperatorExpression nullSetting = new CodeBinaryOperatorExpression(
                settingRef,                
                CodeBinaryOperatorType.IdentityInequality,
                new CodePrimitiveExpression(null));

            CodeBinaryOperatorExpression emptySetting = new CodeBinaryOperatorExpression(
                settingRef,
                CodeBinaryOperatorType.IdentityInequality,
                new CodePrimitiveExpression(""));

            CodeConditionStatement validSetting = new CodeConditionStatement();
            validSetting.Condition = new CodeBinaryOperatorExpression(
                nullSetting,
                CodeBinaryOperatorType.BooleanAnd,
                emptySetting);

            CodeAssignStatement settingToLower = new CodeAssignStatement(settingRef,
                new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(settingRef, "ToLower")));

            CodeVariableDeclarationStatement asmxFileNames =
                new CodeVariableDeclarationStatement(typeof(string), "asmxFileNames");

            asmxFileNames.Type.ArrayElementType = new CodeTypeReference(typeof(string));
            asmxFileNames.Type.ArrayRank = 1;

            asmxFileNames.InitExpression = new CodeMethodInvokeExpression(
                new CodeMethodReferenceExpression(settingRef, "Split"),
                new CodeArrayCreateExpression(typeof(char), new CodeExpression[] { new CodePrimitiveExpression(';') }));

            CodeVariableReferenceExpression asmxFileNamesRef = 
                new CodeVariableReferenceExpression("asmxFileNames");

            CodeVariableDeclarationStatement i = new CodeVariableDeclarationStatement(typeof(int), "i");
            i.InitExpression = new CodePrimitiveExpression(0);
            CodeVariableReferenceExpression iRef = new CodeVariableReferenceExpression("i");            
            CodeBinaryOperatorExpression loopCondition = new CodeBinaryOperatorExpression(
                iRef, CodeBinaryOperatorType.LessThan, 
                new CodePropertyReferenceExpression(asmxFileNamesRef, "Length"));            

            CodeIterationStatement loopFns = new CodeIterationStatement();
            loopFns.InitStatement = i;
            loopFns.TestExpression = loopCondition;
            loopFns.IncrementStatement = new CodeAssignStatement(iRef,
                new CodeBinaryOperatorExpression(iRef, CodeBinaryOperatorType.Add,
                new CodePrimitiveExpression(1)));

            CodeVariableDeclarationStatement fn = new CodeVariableDeclarationStatement(
                typeof(string), "fn");

            fn.InitExpression = new CodeIndexerExpression(asmxFileNamesRef, iRef);
            CodeVariableReferenceExpression fnRef = new CodeVariableReferenceExpression("fn");

            CodeConditionStatement matchPath = new CodeConditionStatement();
            CodeBinaryOperatorExpression ex1 = new CodeBinaryOperatorExpression(
                new CodeMethodInvokeExpression(requestPathRef, "IndexOf",
                new CodeBinaryOperatorExpression(fnRef, 
                    CodeBinaryOperatorType.Add, new CodePrimitiveExpression("?wsdl"))),
                CodeBinaryOperatorType.GreaterThan,
                new CodePrimitiveExpression(0));

            CodeBinaryOperatorExpression ex2 = new CodeBinaryOperatorExpression(
                new CodeMethodInvokeExpression(requestPathRef, "IndexOf",
                new CodeBinaryOperatorExpression(fnRef, 
                    CodeBinaryOperatorType.Add, new CodePrimitiveExpression("?disco"))),
                CodeBinaryOperatorType.GreaterThan,
                new CodePrimitiveExpression(0));

            CodeBinaryOperatorExpression condition = new CodeBinaryOperatorExpression(
                ex1, CodeBinaryOperatorType.BooleanOr, ex2);

            matchPath.Condition = condition;

            CodeThrowExceptionStatement httpException = new CodeThrowExceptionStatement(
                new CodeObjectCreateExpression("HttpException",
                new CodeExpression[] {new CodePrimitiveExpression(404), 
									 new CodePrimitiveExpression(string.Empty)}));

            matchPath.TrueStatements.Add(httpException);
            loopFns.Statements.Add(fn);
            loopFns.Statements.Add(matchPath);
            validSetting.TrueStatements.Add(settingToLower);
            validSetting.TrueStatements.Add(asmxFileNames);
            validSetting.TrueStatements.Add(loopFns);

            appBeginRequest.Statements.Add(validSetting);

            // Add the member to the class.
            asmxHelpModule.Members.Add(appBeginRequest);

            // Add the Init method.
            CodeMemberMethod init = new CodeMemberMethod();
            init.Name = "Init";
            init.ReturnType = new CodeTypeReference(typeof(void));
            init.Attributes = MemberAttributes.Public;
            init.ImplementationTypes.Add("System.Web.IHttpModule");
            init.Parameters.Add(new CodeParameterDeclarationExpression("HttpApplication", "app"));

            // Create the attach event statement.
            CodeDelegateCreateExpression createDelegate = new CodeDelegateCreateExpression(
                new CodeTypeReference("System.EventHandler"), new CodeThisReferenceExpression(),
                appBeginRequest.Name);
            CodeAttachEventStatement attachEvent = new CodeAttachEventStatement(
                new CodeArgumentReferenceExpression("app"), "BeginRequest", createDelegate);

            init.Statements.Add(attachEvent);

            // Add the member to the class.
            asmxHelpModule.Members.Add(init);

            // Add the Dispose method.
            CodeMemberMethod dispose = new CodeMemberMethod();
            dispose.Name = "Dispose";
            dispose.ReturnType = new CodeTypeReference(typeof(void));
            dispose.Attributes = MemberAttributes.Public;
            dispose.ImplementationTypes.Add("System.Web.IHttpModule");
            asmxHelpModule.Members.Add(dispose);

            // Create the namespace and add the new asmxHelpModule to that. 
            string ns = "";
            if (this._language != "vb")
            {
                ns = this._assemblyRootNamespace;
            }

            CodeNamespace cns = new CodeNamespace(ns);
            cns.Imports.Add(new CodeNamespaceImport("System"));
            cns.Imports.Add(new CodeNamespaceImport("System.Web"));
            cns.Imports.Add(new CodeNamespaceImport("System.Configuration"));
            cns.Types.Add(asmxHelpModule);
            
            // Create the SourceFile object.
            SourceFile asmxHelpModuleFile = new SourceFile();
            asmxHelpModuleFile.FileName = asmxHelpModule.Name + "." + _language;
            asmxHelpModuleFile.SourceCode = GenerateCodeFromNamespace(cns);
            asmxHelpModuleFile.AsmxHelpModule = true; // BDS: Set AsmxHelpModule property (This is a newly added one). 
            _genFiles.Add(asmxHelpModuleFile); // Finally add the SourceFile object to the SourceFiles collection.

            // Modify the web.config to:
            // 1. Attach the new AsmxHelpModule in to the Http pipe line.                
            // 2. Attach the forbidden handler to handle the requests to the AsmxHelpPage.aspx file.
            // 3. Set the asmx help file generator to AsmxHelpPage.aspx.
            // 4. Add/Remove the application setting for disabling the WSDL generation.
            // BDS: Fixed a bug here. We did not check for the blank namespace names when generating VB code.
            // We must check whether there is a assemblyRootNamespace specified and apply it appropriatly.		
            ConfigHelper.AttachHttpModule(_appConfigFile + @"\Web.config",
                ns, "AsmxHelpModule", _assemblyName);           
            
			ConfigHelper.AttachHttpHandler(_appConfigFile + @"\Web.config",
				"AsmxHelpPage.aspx", "System.Web.HttpForbiddenHandler");
			
            ConfigHelper.SetAsmxHelpFileGenerator(_appConfigFile + @"\Web.config","AsmxHelpPage.aspx");
            
            if (_enableWsdl)
            {
                ConfigHelper.RemoveDisableWsdl(_appConfigFile + @"\Web.config",
                    _stubFile + ".asmx");
            }
            else
            {
                ConfigHelper.AddDisableWsdl(_appConfigFile + @"\Web.config",
                    _stubFile + ".asmx");
            }
		}

		/// <summary>
		/// This helper method is used to Generate the source code from a CodeNamespace and
		/// return it as a string.
		/// </summary>
		/// <param name="cns">CodeNamespace to be used for code generation.</param>
		/// <returns>A string containing the generated source code.</returns>
		/// <author>BDS - thinktecture</author>
		private string GenerateCodeFromNamespace(CodeNamespace cns)
		{
			// Validate the parameters. 
			Debug.Assert((cns != null),"Parameter cns could not be null");

			StringBuilder sourcecode = new StringBuilder();
			StringWriter sw = new StringWriter(sourcecode, CultureInfo.CurrentCulture);
			_codeGenerator.GenerateCodeFromNamespace(cns, sw, _codeGenOptions);
			
			return GetCodeBanner() + sourcecode.ToString();
		}

		/// <summary>
		/// This method generates an interface for a given type.
		/// </summary>
		/// <param name="type">
		/// CodeTypeDeclaration object to be used for the interface generation.
		/// </param>
		/// <param name="cns">
		/// CodeNamespace object to  be used to gegnerate the namespace details for the interface.
		/// </param>
		/// <returns>A string containing the name of the interface generated.</returns>
		private string GenerateInterfaceForType(CodeTypeDeclaration type, CodeNamespace cns)
		{
			// BDS: Removed the calls to GetPascalCaseName method. When it comes to this step, all the necessary
			// naming convensions are done. So we just have to use the same names in the interface.
			string iname = "I" +  type.Name; // CodeDecorator.GetPascalCaseName(type.Name);
			CodeTypeDeclaration typeInterface = new CodeTypeDeclaration(iname);
			typeInterface.IsClass = false;
			typeInterface.TypeAttributes = TypeAttributes.Public;
			typeInterface.IsInterface = true;
						
			// Add the members to the interface
			foreach(CodeTypeMember member in type.Members)
			{
				// Skip if the current member is a constructor.
				CodeConstructor ctor = member as CodeConstructor;
				if(ctor != null) continue; 

				// Skip if the current member is not a method or if it is a 
				// async method.
				CodeMemberMethod method = member as CodeMemberMethod;
				if((method != null) && (method.ReturnType.BaseType != "System.IAsyncResult"))
				{								
					CodeMemberMethod imethod = new CodeMemberMethod();
					imethod.Name = method.Name;//CodeDecorator.GetPascalCaseName(method.Name);
					imethod.ReturnType = method.ReturnType;
									
					bool isAsyncMethod = false;
					foreach(CodeParameterDeclarationExpression param in method.Parameters)
					{
						// Check whether the method is an async end method call.
						if(param.Type.BaseType != "System.IAsyncResult")
						{							
							CodeParameterDeclarationExpression imethodParam = 
								new CodeParameterDeclarationExpression(param.Type, param.Name);
														
							imethodParam.Type = param.Type;
							imethodParam.Direction = param.Direction;
							imethod.Parameters.Add(imethodParam);							
						}
						else
						{
							isAsyncMethod = true;
							break;
						}
					}

                    isAsyncMethod = (method.CustomAttributes.Count == 0);

					// Add the method only if it is not a async method.
					if(!isAsyncMethod)
						typeInterface.Members.Add(imethod);
				}
			}

			// Convert the parameter names to pascal case.
			// BDS: No longer need to do this.
			// CodeDecorator.UseCamelCaseParameterNames(typeInterface);

			// Generate the source code for the interface.
			CodeNamespace inamespace = new CodeNamespace(cns.Name);

			// Add all the imports from the source namespace.
			foreach(CodeNamespaceImport imp in cns.Imports)
			{
				inamespace.Imports.Add(imp);
			}
			
			// Add the interface declaration to the namespace.
			inamespace.Types.Add(typeInterface);
			
			// Apply the code modifications applicable to interfaces.
			// No longer need to do this.
			//if(_generateCollections)
			//	CodeDecorator.ChangeArraysToCollections(ref inamespace,true);

			// Create the SourceFile.
			SourceFile interfaceFile = new SourceFile();
			interfaceFile.FileName = typeInterface.Name + "." + _language;
			interfaceFile.SourceCode = GenerateCodeFromNamespace(inamespace);
			
			// Finally add the generated file to the SourceFiles collection.
			_genFiles.Add(interfaceFile);

			return iname;
		}

		/// <summary>
		/// This helper method initializes the CodeGenerator and code generation options 
		/// for the selected programming language.
		/// </summary>
		private void InitializeCodeGenerator()
		{
			// Create the code generator.
			CodeDomProvider cp = null;

			if (_language == "cs")
			{
				cp = new CSharpCodeProvider();
			}
			else
			{
				cp = new VBCodeProvider();
			}

			_codeGenerator = cp;

			// Create the code generation options.
			_codeGenOptions = new CodeGeneratorOptions();
			_codeGenOptions.BlankLinesBetweenMembers = true;
			_codeGenOptions.BracingStyle = "C";
		}	

		private void AddWarning(string warningMessage)
		{
			if(this._warnings == null)
			{
				this._warnings = new ArrayList(); 
			}

			this._warnings.Add(warningMessage);
		}
		
		private void CheckBPCompliance()
		{
			if(WsdlHelper.HasRpcEncodedBindings(this._wsdlSource))
			{
				this.AddWarning(
					"This WSDL contains RPC/Encoded style bindings. Therefore this WSDL does not conform to WS-I Basic Profile v1.1.");
			}
		}

        /// <summary>
        /// Adds the necessary elements to web.config file to disable the SOAP 1.2 binding.
        /// </summary>
        private void AddRemoveSoap12Protocol(string configFile)
        {
            foreach (ServiceDescription sd in sdi.ServiceDescriptions)
            {
                foreach (Binding binding in sd.Bindings)
                {
                    object soap12Binding = binding.Extensions.Find(typeof(System.Web.Services.Description.Soap12Binding));
                    if (soap12Binding != null)
                    {
                        ConfigHelper.EnableSoap12(configFile);
                        return;
                    }                        
                }
            }                            

            ConfigHelper.DisableSoap12(configFile);
        }

        /// <summary>
        /// Callback function to be invoked by the schema compiler.
        /// </summary>
        private void CompilerValidationCallback(object sender, ValidationEventArgs args)
        {
            // Add the exceptions message to the current warnings collection.
            AddWarning(args.Message);
        }

        /// <summary>
        /// Checks whether a given XmlSchemaType could be represented as an array. That is the XmlSchemaType
        /// has to be:
        ///     1. Complex type
        ///     2. ...with no base type
        ///     3. ...has no attributes
        ///     4. ...has only one element
        ///     5. ...whose maxOccurs is > 1
        /// </summary>
        /// <returns></returns>
        private bool CouldBeAnArray(XmlSchemaType schematype)
        {
            XmlSchemaComplexType complextype = schematype as XmlSchemaComplexType;
            if (complextype != null)
            {
                if (complextype.Attributes.Count == 0)
                {
                    XmlSchemaSequence sequence = complextype.Particle as XmlSchemaSequence;
                    if (sequence != null)
                    {
                        if (sequence.Items.Count == 1)
                        {
                            XmlSchemaElement element = sequence.Items[0] as XmlSchemaElement;
                            if (element != null)
                            {
                                if (element.MaxOccurs > 1 || (element.MaxOccursString != null && element.MaxOccursString.ToLower() == "unbounded"))
                                {
                                    return true;
                                }
                            }
                        }
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Removes the duplicate schemas in a given XmlSchemaSet instance.
        /// </summary>
        private void RemoveDuplicates(ref XmlSchemaSet set)
        {
            ArrayList schemalist = new ArrayList(set.Schemas());
            ArrayList duplicatedschemalist = new ArrayList();
            for (int schemaindex = 0; schemaindex < schemalist.Count; schemaindex++)
            {
                if (((XmlSchema)schemalist[schemaindex]).SourceUri == string.Empty)
                    duplicatedschemalist.Add((XmlSchema)schemalist[schemaindex]);
                else
                {
                    for (int lowerschemaindex = schemaindex + 1; lowerschemaindex < schemalist.Count; lowerschemaindex++)
                    {
                        if (((XmlSchema)schemalist[lowerschemaindex]).SourceUri == 
                            ((XmlSchema)schemalist[schemaindex]).SourceUri)
                        {
                            duplicatedschemalist.Add((XmlSchema)schemalist[lowerschemaindex]);
                        }
                    }
                }
            }
            foreach (XmlSchema schema in duplicatedschemalist)
            {
                set.Remove(schema);
            }
        }
    }
}
